<template>
  <div id="app">
    <div class="view-toolbar">
      <button
        :class="{ active: viewMode === 'sphere' }"
        @click="switchViewMode('sphere')"
      >
        球面全景（单图）
      </button>
      <button
        :class="{ active: viewMode === 'cube' }"
        @click="switchViewMode('cube')"
      >
        立方体全景（6 图）
      </button>
    </div>
    <div class="summary-card">
      <h3>当前模式：{{ modeInfo.title }}</h3>
      <p>{{ modeInfo.desc }}</p>
    </div>

    <div v-if="viewMode === 'sphere'" class="cube-row sphere-mode">
      <SpherePanorama
        v-if="rightSphereReady"
        ref="leftSphereRef"
        class="sphere-panel"
        :texture="singlePanorama"
        :hotspot="null"
        :showHotspot="false"
        :widthSize="widthSize"
        :heightSize="sphereHeight"
        @canvas-ready="() => onSphereCanvasReady('left')"
      />
      <SpherePanorama
        ref="rightSphereRef"
        class="sphere-panel"
        :texture="singlePanorama"
        :hotspot="hotspot"
        :showHotspot="true"
        :widthSize="widthSize"
        :heightSize="sphereHeight"
        @canvas-ready="() => onSphereCanvasReady('right')"
      />
    </div>

    <div v-else class="cube-row">
      <div class="sphere-panel pano-panel" :style="{ height: `${sphereHeight}px` }">
        <PanoramaView
          ref="leftPanoRef"
          :images="images"
          :widthSize="widthSize"
          :heightSize="sphereHeight"
          :world-hotspot="panoHotspot"
          :show-hotspot="false"
          @canvas-ready="onCanvasReady"
        />
      </div>
      <div class="sphere-panel pano-panel" :style="{ height: `${sphereHeight}px` }">
        <PanoramaView
          ref="rightPanoRef"
          :images="images"
          :widthSize="widthSize"
          :heightSize="sphereHeight"
          :world-hotspot="panoHotspot"
          :show-hotspot="true"
          @canvas-ready="onCanvasReady"
        />
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, computed, nextTick, onBeforeUnmount } from 'vue'
import PanoramaView from './components/PanoramaView.vue'
import SpherePanorama from './components/SpherePanorama.vue'
import singlePanorama from '@/assets/全景图一张图.png'

// ---------------------------- 组件引用 ----------------------------------
const leftPanoRef = ref<{
  getCanvas: () => HTMLCanvasElement | null
  getControls: () => any
  getCamera: () => any
} | null>(null)

const rightPanoRef = ref<{
  getCanvas: () => HTMLCanvasElement | null
  getControls: () => any
  getCamera: () => any
} | null>(null)

const leftSphereRef = ref<{
  getCanvas: () => HTMLCanvasElement | null
  getControls: () => any
  getCamera: () => any
} | null>(null)

const rightSphereRef = ref<{
  getCanvas: () => HTMLCanvasElement | null
  getControls: () => any
  getCamera: () => any
} | null>(null)

const widthSize = 900
const viewMode = ref<'sphere' | 'cube'>('sphere')

interface PixelHotspot {
  x: number
  y: number
}

const sphereHeight = 520
const singlePanoFallbackSize = { width: 4096, height: 2048 }
const hotspot = ref<PixelHotspot>({
  x: Math.round(singlePanoFallbackSize.width / 2),
  y: Math.round(singlePanoFallbackSize.height / 2),
})
const panoHotspot = ref({ x: 10, y: 5, z: 15 })

const modeInfo = computed(() => {
  if (viewMode.value === 'sphere') {
    return {
      title: '球面全景（单张 equirectangular）',
      desc: '右侧使用单张全景图贴在反向球体上并带热点，左侧实时同步，用于展示单纹理方案。',
    }
  }
  return {
    title: '立方体全景（六面体贴图）',
    desc: '两个 PanoramaView 共用 6 张贴图，通过 OrbitControls 交互并保持相机同步，用于对比传统方案。',
  }
})

// ---------------------------- 全景图资源（立方体）----------------------------------
import right from '@/assets/park/posx.jpg'
import left from '@/assets/park/negx.jpg'
import top from '@/assets/park/posy.jpg'
import bottom from '@/assets/park/negy.jpg'
import front from '@/assets/park/posz.jpg'
import back from '@/assets/park/negz.jpg'
const images = ref([right, left, top, bottom, front, back])

const updateHotspotWithinCenter = (width: number, height: number) => {
  if (!width || !height) return
  const minX = width * 0.45
  const maxX = width * 0.55
  const minY = height * 0.45
  const maxY = height * 0.55
  hotspot.value = {
    x: Math.round(minX + Math.random() * (maxX - minX)),
    y: Math.round(minY + Math.random() * (maxY - minY)),
  }
}

const preloadSinglePanorama = () => {
  const img = new Image()
  img.src = singlePanorama
  img.onload = () => {
    updateHotspotWithinCenter(img.naturalWidth, img.naturalHeight)
  }
  img.onerror = () => {
    updateHotspotWithinCenter(singlePanoFallbackSize.width, singlePanoFallbackSize.height)
  }
}

preloadSinglePanorama()

// 状态管理
const readyCount = ref(0)
let rightCanvas: HTMLCanvasElement | null = null
const sphereReady = reactive({ left: false, right: false })
let rightSphereCanvas: HTMLCanvasElement | null = null
let rightSphereControls: any = null
const rightSphereReady = ref(false)

// Canvas就绪处理
const onCanvasReady = async () => {
  readyCount.value++

  // 当两个组件都就绪时，先同步初始视角，再绑定增量同步事件
  if (readyCount.value === 2) {
    await nextTick()
    // 初始化时同步一次绝对位置，让左右都看向热点
    syncInitialCubeViewToLeft()
    // 然后绑定增量同步事件
    bindSyncEvents()
  }
}

// 初始化时同步一次绝对位置，让左右都看向热点
const syncInitialCubeViewToLeft = () => {
  const leftControls = leftPanoRef.value?.getControls()
  const rightControls = rightPanoRef.value?.getControls()
  const leftCamera = leftPanoRef.value?.getCamera()
  const rightCamera = rightPanoRef.value?.getCamera()

  if (!leftControls || !rightControls || !leftCamera || !rightCamera) {
    console.warn('立方体初始化同步失败：无法获取相机或控制器')
    return
  }

  // 完全复制右侧的相机状态到左侧
  leftCamera.position.copy(rightCamera.position)
  leftCamera.quaternion.copy(rightCamera.quaternion)
  leftCamera.fov = rightCamera.fov
  leftCamera.updateProjectionMatrix()

  leftControls.target.copy(rightControls.target)
  leftControls.update()

  console.log('✅ 立方体初始视角同步完成，左右都对齐到热点')
}

// 绑定同步事件
const bindSyncEvents = () => {
  const rightControls = rightPanoRef.value?.getControls()
  if (!rightControls) {
    console.warn('右侧立方体Controls获取失败')
    return
  }
  // 只监听右侧控制器的 change 事件，不强制同步绝对位置
  rightControls.addEventListener('change', syncCameraToLeft)
  console.log('✅ 立方体增量同步事件绑定完成')
}

// 记录右侧立方体上一次的状态，用于计算增量
let lastRightCubeState: {
  azimuth: number
  polar: number
  distance: number
  fov: number
} | null = null

// 同步相机增量到左侧
const syncCameraToLeft = () => {
  const leftControls = leftPanoRef.value?.getControls()
  const rightControls = rightPanoRef.value?.getControls()
  const leftCamera = leftPanoRef.value?.getCamera()
  const rightCamera = rightPanoRef.value?.getCamera()

  if (!leftControls || !rightControls || !leftCamera || !rightCamera) {
    return
  }

  // 计算右侧当前的球坐标（相对于 target）
  const rightOffset = rightCamera.position.clone().sub(rightControls.target)
  const rightDistance = rightOffset.length()
  const rightAzimuth = Math.atan2(rightOffset.x, rightOffset.z)
  const rightPolar = Math.acos(Math.max(-1, Math.min(1, rightOffset.y / rightDistance)))

  // 初始化或计算增量
  if (!lastRightCubeState) {
    lastRightCubeState = {
      azimuth: rightAzimuth,
      polar: rightPolar,
      distance: rightDistance,
      fov: rightCamera.fov
    }
    return
  }

  // 计算右侧的变化量
  const deltaAzimuth = rightAzimuth - lastRightCubeState.azimuth
  const deltaPolar = rightPolar - lastRightCubeState.polar
  const deltaDistance = rightDistance - lastRightCubeState.distance
  const deltaFov = rightCamera.fov - lastRightCubeState.fov

  // 更新记录
  lastRightCubeState = {
    azimuth: rightAzimuth,
    polar: rightPolar,
    distance: rightDistance,
    fov: rightCamera.fov
  }

  // 应用增量到左侧
  const leftOffset = leftCamera.position.clone().sub(leftControls.target)
  const leftDistance = leftOffset.length()
  const leftAzimuth = Math.atan2(leftOffset.x, leftOffset.z)
  const leftPolar = Math.acos(Math.max(-1, Math.min(1, leftOffset.y / leftDistance)))

  const newLeftAzimuth = leftAzimuth + deltaAzimuth
  const newLeftPolar = Math.max(0.01, Math.min(Math.PI - 0.01, leftPolar + deltaPolar))
  const newLeftDistance = Math.max(1, leftDistance + deltaDistance)

  // 重新计算左侧相机位置
  leftCamera.position.x = newLeftDistance * Math.sin(newLeftPolar) * Math.sin(newLeftAzimuth)
  leftCamera.position.y = newLeftDistance * Math.cos(newLeftPolar)
  leftCamera.position.z = newLeftDistance * Math.sin(newLeftPolar) * Math.cos(newLeftAzimuth)
  leftCamera.position.add(leftControls.target)

  // 同步 FOV 增量
  leftCamera.fov = Math.max(10, Math.min(75, leftCamera.fov + deltaFov))
  leftCamera.updateProjectionMatrix()

  leftControls.update()
}

const onSphereCanvasReady = async (side: 'left' | 'right') => {
  sphereReady[side] = true
  if (side === 'right') {
    rightSphereReady.value = true
  }
  if (sphereReady.left && sphereReady.right) {
    await nextTick()
    // 初始化时先同步一次绝对位置，让左右都看向热点
    syncInitialViewToLeft()
    // 然后绑定增量同步事件
    bindSphereSyncEvents()
  }
}

const resetSphereState = () => {
  sphereReady.left = false
  sphereReady.right = false
  rightSphereReady.value = false
  lastRightState = null
  cleanupSphereEvents()
}

const switchViewMode = (mode: 'sphere' | 'cube') => {
  if (viewMode.value === mode) return
  viewMode.value = mode
  if (mode === 'sphere') {
    resetSphereState()
    cleanupCubeEvents()
  } else {
    cleanupSphereEvents()
    resetCubeState()
  }
}

const resetCubeState = () => {
  readyCount.value = 0
  lastRightCubeState = null
}

const cleanupCubeEvents = () => {
  const rightControls = rightPanoRef.value?.getControls()
  if (rightControls) {
    rightControls.removeEventListener?.('change', syncCameraToLeft)
  }
  rightCanvas = null
  lastRightCubeState = null
}

const cleanupSphereEvents = () => {
  if (rightSphereControls) {
    rightSphereControls.removeEventListener?.('change', syncSphereCameraToLeft)
    rightSphereControls = null
  }
  rightSphereCanvas = null
  lastRightState = null
}

// 初始化时同步一次绝对位置，让左右都看向热点
const syncInitialViewToLeft = () => {
  const leftControls = leftSphereRef.value?.getControls()
  const rightControls = rightSphereRef.value?.getControls()
  const leftCamera = leftSphereRef.value?.getCamera()
  const rightCamera = rightSphereRef.value?.getCamera()

  if (!leftControls || !rightControls || !leftCamera || !rightCamera) {
    console.warn('初始化同步失败：无法获取相机或控制器')
    return
  }

  // 完全复制右侧的相机状态到左侧
  leftCamera.position.copy(rightCamera.position)
  leftCamera.quaternion.copy(rightCamera.quaternion)
  leftCamera.fov = rightCamera.fov
  leftCamera.updateProjectionMatrix()

  leftControls.target.copy(rightControls.target)
  leftControls.update()

  console.log('✅ 初始视角同步完成，左右都对齐到热点')
}

const bindSphereSyncEvents = () => {
  cleanupSphereEvents()
  rightSphereCanvas = rightSphereRef.value?.getCanvas() || null
  rightSphereControls = rightSphereRef.value?.getControls() || null

  if (!rightSphereControls) {
    console.warn('右侧球面Controls获取失败')
    return
  }

  // 只监听右侧控制器的 change 事件，不强制同步绝对位置
  rightSphereControls.addEventListener('change', syncSphereCameraToLeft)
  console.log('✅ 球面增量同步事件绑定完成')
}

// 记录右侧上一次的状态，用于计算增量
let lastRightState: {
  azimuth: number
  polar: number
  distance: number
  fov: number
} | null = null

const syncSphereCameraToLeft = () => {
  const leftControls = leftSphereRef.value?.getControls()
  const rightControls = rightSphereRef.value?.getControls()
  const leftCamera = leftSphereRef.value?.getCamera()
  const rightCamera = rightSphereRef.value?.getCamera()

  if (!leftControls || !rightControls || !leftCamera || !rightCamera) {
    return
  }

  // 计算右侧当前的球坐标（相对于 target）
  const rightOffset = rightCamera.position.clone().sub(rightControls.target)
  const rightDistance = rightOffset.length()
  const rightAzimuth = Math.atan2(rightOffset.x, rightOffset.z)
  const rightPolar = Math.acos(Math.max(-1, Math.min(1, rightOffset.y / rightDistance)))

  // 初始化或计算增量
  if (!lastRightState) {
    lastRightState = {
      azimuth: rightAzimuth,
      polar: rightPolar,
      distance: rightDistance,
      fov: rightCamera.fov
    }
    return
  }

  // 计算右侧的变化量
  const deltaAzimuth = rightAzimuth - lastRightState.azimuth
  const deltaPolar = rightPolar - lastRightState.polar
  const deltaDistance = rightDistance - lastRightState.distance
  const deltaFov = rightCamera.fov - lastRightState.fov

  // 更新记录
  lastRightState = {
    azimuth: rightAzimuth,
    polar: rightPolar,
    distance: rightDistance,
    fov: rightCamera.fov
  }

  // 应用增量到左侧
  const leftOffset = leftCamera.position.clone().sub(leftControls.target)
  const leftDistance = leftOffset.length()
  const leftAzimuth = Math.atan2(leftOffset.x, leftOffset.z)
  const leftPolar = Math.acos(Math.max(-1, Math.min(1, leftOffset.y / leftDistance)))

  const newLeftAzimuth = leftAzimuth + deltaAzimuth
  const newLeftPolar = Math.max(0.01, Math.min(Math.PI - 0.01, leftPolar + deltaPolar))
  const newLeftDistance = Math.max(1, leftDistance + deltaDistance)

  // 重新计算左侧相机位置
  leftCamera.position.x = newLeftDistance * Math.sin(newLeftPolar) * Math.sin(newLeftAzimuth)
  leftCamera.position.y = newLeftDistance * Math.cos(newLeftPolar)
  leftCamera.position.z = newLeftDistance * Math.sin(newLeftPolar) * Math.cos(newLeftAzimuth)
  leftCamera.position.add(leftControls.target)

  // 同步 FOV 增量
  leftCamera.fov = Math.max(10, Math.min(75, leftCamera.fov + deltaFov))
  leftCamera.updateProjectionMatrix()

  leftControls.update()
}

onBeforeUnmount(() => {
  cleanupSphereEvents()
  cleanupCubeEvents()
})
</script>

<style scoped>
#app {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  align-items: center;
  gap: 24px;
}

.cube-row {
  display: flex;
  gap: 16px;
}

.sphere-panel {
  width: 100%;
  max-width: 900px;
}
</style>
